package sky.cin.framework.sfoc.core;

import sky.cin.framework.sfoc.core.annotations.beans.Autowired;
import sky.cin.framework.sfoc.core.annotations.beans.Value;
import sky.cin.framework.sfoc.core.common.Context;
import sky.cin.framework.sfoc.core.common.utils.BeanUtils;
import sky.cin.framework.sfoc.core.interfaces.*;

import java.lang.reflect.Field;

public class BaseBeanCreator implements BeanCreator {
    
    private final Context context;
    
    public BaseBeanCreator(Context context) {
        this.context = context;
    }
    
    public Object doCreateBean(String beanName, BeanDefinition definition) {
        return doCreateBean(beanName, definition, null);
    }
    
    @Override
    public Object doCreateBean(String beanName, BeanDefinition definition, Object bean) {
        if (bean != null) {
            return bean;
        }
        try {
            // 创建对象
            Class<?> clazz = definition.getClazz();
            if (clazz.isInterface()) {
                bean = newInstance(clazz);
            } else {
                bean = clazz.newInstance();
            }
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                checkAutowired(bean, field);
                checkValue(bean, field);
            }
            checkBeanNameAware(beanName, bean);
            bean = postProcessBeforeInitialization(bean, beanName);
            bean = checkInitializingBean(bean);
            bean = postProcessAfterInitialization(bean, beanName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }
    
    
    private void checkValue(Object bean, Field field) throws IllegalAccessException {
        if (BeanUtils.isAnnotationPresent(field, Value.class)) {
            String value = BeanUtils.getAnnotation(field, Value.class).value();
            value = value.substring(2, value.length() - 1);
            Object val = Context.getApplicationConfig().get(value);
            field.setAccessible(true);
            field.set(bean, val);
        }
    }
    
    private void checkAutowired(Object bean, Field field) throws IllegalAccessException {
        if (BeanUtils.isAnnotationPresent(field, Autowired.class)) {
            String fieldBeanName = BeanUtils.getBeanName(field.getType());
            Object fieldBean = context.getBean(fieldBeanName);
            field.setAccessible(true);
            field.set(bean, fieldBean);
        }
    }
    
    private void checkBeanNameAware(String beanName, Object bean) {
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
    }
    
    private Object checkInitializingBean(Object bean) {
        if (bean instanceof InitializingBean) {
            ((InitializingBean) bean).afterPropertiesSet();
            bean = null;
        }
        return bean;
    }
    
    private Object postProcessBeforeInitialization(Object bean, String beanName) {
        for (BeanPostProcessor processor : context.getProcessors()) {
            bean = processor.postProcessBeforeInitialization(bean, beanName);
        }
        return bean;
    }
    
    private Object postProcessAfterInitialization(Object bean, String beanName) {
        for (BeanPostProcessor processor : context.getProcessors()) {
            bean = processor.postProcessAfterInitialization(bean, beanName);
        }
        return bean;
    }
    
    private Object newInstance(Class<?> clazz) {
        Object bean = null;
        for (BeanInterfaceFactory factory : context.getFactories()) {
            bean = factory.newProxy(clazz, factory);
            if (bean != null) {
                break;
            }
        }
        return bean;
    }
    
}
